#ifndef _FILE_MEM_H_
#define _FILE_MEM_H_

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "file.h"

typedef struct file_mem_t{
    uint8_t     *data;
    int         alloc_size;
    int         cur_pos;
    int         write_pos;
}file_mem_t;

static file_mem_t* file_mem_new(int size){
    file_mem_t *mem = (file_mem_t*)calloc(1,sizeof(*mem));
    mem->data = (uint8_t*)calloc(1,size);
    mem->alloc_size = size;
    return mem;
}

static uint8_t* file_mem_get_cur_ptr(file_mem_t*fm,int copysize){
    if(fm->cur_pos+copysize>fm->alloc_size){
        //申请一倍的数据
        int size = //fm->cur_pos + copysize;
            fm->alloc_size*2;
        while(size<fm->cur_pos + copysize){
            size *= 2;
        }
        fm->alloc_size = size;
        fm->data = (uint8_t*)realloc(fm->data,size);
    }
    return fm->data + fm->cur_pos;
}

static size_t file_mem_write(void*data,size_t elementSize,size_t c,void*ud){
    file_mem_t *fm = (file_mem_t*)ud;
    uint8_t*ptr = file_mem_get_cur_ptr(fm,elementSize*c);
    memcpy(ptr,data,elementSize*c);
    fm->cur_pos += elementSize * c;
    if(fm->cur_pos>fm->write_pos){
        fm->write_pos = fm->cur_pos;
    }
    return c;
}

static size_t file_mem_read(void*data,size_t elementSize,size_t c,void*ud){
    file_mem_t *fm = (file_mem_t*)ud;
    if(fm->cur_pos>=fm->write_pos){
        return 0;
    }
    while(fm->cur_pos + elementSize*c>fm->write_pos){
        if(c<=0){
            return c;
        }
        c -= 1;
    }
    uint8_t*ptr = file_mem_get_cur_ptr(fm,0);
    memcpy(data,ptr,elementSize*c);
    fm->cur_pos += elementSize*c;
    return c;
}

static long file_mem_tell(void*ud){
    file_mem_t *fm = (file_mem_t*)ud;
    return fm->cur_pos;
}

static void file_mem_close(void*ud){
    file_mem_t *fm = (file_mem_t*)ud;
    free(fm->data);
    free(fm);
}

static int file_mem_seek(void*ud,long pos,int org){
    file_mem_t *fm = (file_mem_t*)ud;
    long pp = fm->cur_pos + pos;
    switch (org)
    {
    case SEEK_SET:
        pp = pos;
        break;
    case SEEK_END:
        pp = fm->write_pos + pos;
        break;
    case SEEK_CUR:
        pp = fm->cur_pos + pos;
        break;
    default:
        return 0;
        break;
    }


    if(pp<0){
        return 1;
    }
    else if(pp>fm->write_pos){
        return 1;
    }
    fm->cur_pos = pp;
    return 0;
}

static file_t* file_memopen(int size){
    if(size<=0){
        size = 512;
    }
    return file_new(file_mem_new(size),"memory",file_mem_close,file_mem_write,file_mem_read,file_mem_seek,file_mem_tell);
}

#endif
